package com.mayikt.auth.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.mayikt.auth.mapper.AppInfoMapper;
import com.mayikt.auth.mapper.entity.AccessTokenRecord;
import com.mayikt.auth.mapper.entity.AppInfo;
import com.mayikt.auth.service.AccessTokenRecordService;
import com.mayikt.auth.service.AuthorizationService;
import com.mayikt.base.BaseApiService;
import com.mayikt.base.BaseResponse;
import com.mayikt.base.enums.ApiResponseEnum;
import com.mayikt.core.util.RedisUtils;
import com.mayikt.exception.BizException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.UUID;

@RestController
@Service
public class AuthorizationServiceImpl extends BaseApiService<JSONObject> implements AuthorizationService {

    @Autowired
    private AppInfoMapper appInfoMapper;
    @Autowired
    private AccessTokenRecordService accessTokenRecordService;
    @Autowired
    private RedisUtils redisUtils;

    private final static String SECRET_KEY = "!@#$xun";
    private final static String PRE_ACCESSTOKEN = "access_token";
    private final static Long ACCESSTOKEN_EXPIRED_TIME = 60*60*2L; //accessToken过期时间
    /**
     * 通过应用名appName申请商户号
     *
     * @param appName 应用名
     * @return
     */
    @Override
    public BaseResponse<JSONObject> applyAppInfo(String appName) {
        if (StrUtil.isEmpty(appName)){
            return setResultError("应用名不能为空！");
        }
        String appId = UUID.randomUUID().toString();
        String secret = SecureUtil.md5(SECRET_KEY+appId).toLowerCase();

        AppInfo appInfo = new AppInfo();
        appInfo.setAppId(appId);
        appInfo.setAppSecret(secret);
        appInfo.setAppName(appName);
        appInfo.setCreateTime(LocalDateTime.now());
        appInfo.setCreateTime(appInfo.getCreateTime());
        int row = appInfoMapper.insert(appInfo);
        if (row <= 0){
            return setResultError("申请失败！");
        }
        JSONObject data = new JSONObject();
        data.put("appId",appId);
        data.put("secret",secret);
        return setResultSuccess(data);
    }

    /**
     * 通过商户号appId 和 密钥secret 获取accessToken
     *
     * @param appId  商户号
     * @param secret  密钥
     * @return
     */
    @Override
    @Transactional
    public BaseResponse<JSONObject> getAccessToken(String appId, String secret) {
        if (StrUtil.isEmpty(appId)){
            throw new BizException(ApiResponseEnum.PARAMETER_CAN_NOT_NULL,"appId不能为空");
        }
        if (StrUtil.isEmpty(secret)){
            throw new BizException(ApiResponseEnum.PARAMETER_CAN_NOT_NULL,"secret不能为空");
        }
        QueryWrapper<AppInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(AppInfo::getAppId,appId).eq(AppInfo::getAppSecret,secret);
        AppInfo appInfo = appInfoMapper.selectOne(queryWrapper);
        if (BeanUtil.isEmpty(appInfo)){
            throw new BizException(ApiResponseEnum.DATA_NULL,"参数错误，不存在相关数据");
        }
        //生成token之前，把之前的token全部置为失效状态
        UpdateWrapper<AccessTokenRecord> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(AccessTokenRecord::getAvailable,'0').eq(AccessTokenRecord::getAppId,appId);
        accessTokenRecordService.updateAvailableByAppId(0,appId);

        String accessToken = SecureUtil.md5(PRE_ACCESSTOKEN+UUID.randomUUID());
        JSONObject redisAppInfo = new JSONObject();
        redisAppInfo.put("appId",appId);
        redisAppInfo.put("secret",secret);

        //向redis和数据库中存入accessToken
        redisUtils.set(accessToken,redisAppInfo,ACCESSTOKEN_EXPIRED_TIME);
        AccessTokenRecord accessTokenRecord = new AccessTokenRecord();
        accessTokenRecord.setAccessToken(accessToken);
        accessTokenRecord.setAppId(appId);
        accessTokenRecord.setAvailable(1);
        accessTokenRecord.setCreateTime(LocalDateTime.now());
        boolean row = accessTokenRecordService.save(accessTokenRecord);
        if (!row){
            setResultError("获取accessToken失败");
        }
        JSONObject data = new JSONObject();
        data.put("accessToken",accessToken);
        return setResultSuccess(data);
    }

    /**
     * 验证accessToken是否有效
     *
     * @param accessToken
     * @return
     */
    @Override
    public BaseResponse<JSONObject> getAppInfo(String accessToken) {
        //先去redis中查询是否存在，不存在则说明已过期
        //若存在则说明有效，但是还要通过数据库查看是否已经被置为无效，如果无效
        //如果有效，那么通过appId查询相关信息
        if (BeanUtil.isEmpty(accessToken)){
            return setResultError("accessToken不能为空");
        }
        JSONObject jsonObject = (JSONObject) redisUtils.get(accessToken);
        if (BeanUtil.isEmpty(jsonObject)){
            return setResultError("accessToken已失效，请重新申请");
        }
        //redis存在，判断数据库是否有效
        QueryWrapper<AccessTokenRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(AccessTokenRecord::getAccessToken,accessToken);
        AccessTokenRecord accessTokenRecord = accessTokenRecordService.getOne(queryWrapper);
        if (0 == accessTokenRecord.getAvailable()){
            return setResultError("accessToken已失效，请重新申请");
        }
        //查询对应的appInfo返回
        QueryWrapper<AppInfo> appInfoQueryWrapper = new QueryWrapper<>();
        appInfoQueryWrapper.lambda().eq(AppInfo::getAppId,accessTokenRecord.getAppId());
        AppInfo appInfo = appInfoMapper.selectOne(appInfoQueryWrapper);
        if (BeanUtil.isEmpty(appInfo)){
            return setResultError("未找到对应的商户信息");
        }
        JSONObject data = new JSONObject();
        data.put("appInfo",appInfo);
        return setResultSuccess(data);
    }
}
